#define CMDHI_COUNT               16    // high-level message types
#define CMDHI_MASK              0x0f    // bits to use for command identifier in cmd byte, remainder might carry data
#define CMDHI_INIT                 0    // long join - with a join point
#define CMDHI_ESTUPDATE            1    // update to estimations of selectivities
#define CMDHI_CONFIRMJOIN          2    // target nominates a join point
#define CMDHI_CONFIRMSOURCE        3    // join point informs source about itself and target
#define CMDHI_CONFIRMTARGETSOURCE  4    // target node notifies source directly to join at base
#define CMDHI_TUPLE                5    // send a data value from source/target to join, or from join to Base
#define CMDHI_REPAIR               6    // used with RepairRoute
#define CMDHI_PATHCOLLAPSE         7    // used for path collapsing
#define CMDHI_COORDCOST            8    // coordinator cost distribution msg
#define CMDHI_COORDDECISION        9    // coordinator decision msg
#define CMDHI_COORDPAIRS          10    // notify base station for pairs
#define CMDHI_SUMMARYSPEC         11    // summary and attribute specification
#define CMDHI_ATTRUPDATE          12
#define CMDHI_QUERY               13    // disseminate and start/stop query
#define CMDHI_TREESDONE           14    // notify base station for completed tree construction
#define CMDHI_STATS               15

#ifdef MINIMUM_MEMORY
  #define MAX_PATHLEN            15  // maximum number of hops per path
  #define MAX_ROUTEDATASIZE      20  // maximum payload for a routing payload
  #define MAX_BASEFLOODDATASIZE  50  // maximum payload for a base flood payload
  #define ROUTEBUFFER_SIZE       20  // maximum number of routes through a node (no eviction)
  #define PAYLOADBUFFER_SIZE      5
#else
  #define MAX_PATHLEN              30
  #define MAX_ROUTEDATASIZE        50
  #define MAX_BASEFLOODDATASIZE   225   // should be able to fit a large query
  #define ROUTEBUFFER_SIZE        100
  #define PAYLOADBUFFER_SIZE       10
#endif

#define MAX_REPAIRPATHLEN  MAX_PATHLEN // max length of path after point of failure to use in RepairRoute
#define MAX_FAILRETRY      0   // maximum number of retries of the failed node

#define ROUTEMSG_FORWARD 0      // Seq. routing (FindFirst) message is in exploration mode
#define ROUTEMSG_RETURN  1      // Seq. routing in backtrack mode

#define IS_IDROUTE(msg)  (msg->predicate_count == 1 && summary_desc[msg->predicates[0].summary_index].attrdesc_index == ATTR_ID)
#define MAX_PREDICATES   4

#define PAYLOADBUFFER_THROTTLE    3

typedef struct {
  uint8_t summary_index;           // destination summary id
  ATTRIBUTE value;                 // value of the attribute
  uint8_t value_ext;               // sent only if attribute is RTREE, then it is the radius)
} ROUTING_PREDICATE;

typedef struct {                   // Message type for Seq. Route and Flood route.
  uint8_t cmd;                     // command identifier specifies if this is a seq. or flood route

  uint8_t route_id;                // if highest bit set: repair, otherwise regular route

  uint8_t predicate_count;         // list of (attribute, value) pairs
  ROUTING_PREDICATE predicates[MAX_PREDICATES]; // predicate currenly AND on all pairs

  uint8_t tree_index;              // tree on which the request is ascending (UpTree)
  ADDRESS source_node;             // request initiator

  uint8_t path_len;                // length of path from source
  ADDRESS path[MAX_PATHLEN];       // path from source (including the source node itself)
  uint8_t top_index;               // node on the path, from which the path starts to descend
                          // index of shortcut node
  int16_t shortcut_index; // not sent, set by shortcutting function, used by handle_dataintermediate

  uint8_t data_len;                // length of payload
  uint8_t data[MAX_ROUTEDATASIZE];  // payload

  uint8_t is_repair;
  uint8_t repair_hoplimit;    // send only if repair msg
  ADDRESS failed_node;  // sent only if repair: which node is failed
  uint8_t fail_count;   // sent only if repair: how many times the node has failed
  uint8_t repair_pathlen; // sent only if repair: length of path segment after the failure to destination
  ADDRESS repair_path[MAX_REPAIRPATHLEN]; // sent only if repair: path segment after failure
} PACKING_MACRO ROUTE_MSG;

typedef struct {
  uint8_t cmd;

  uint8_t predicate_count;         // list of (attribute, value) pairs
  ROUTING_PREDICATE predicates[MAX_PREDICATES]; // predicate currenly AND on all pairs

  ADDRESS source_node;
  uint8_t data_len;           // length of payload
  uint8_t data[MAX_BASEFLOODDATASIZE];  // payload
} PACKING_MACRO BASEFLOOD_MSG;

typedef struct {             // Structure for maintaining route requests through a node
  uint8_t used;

  uint8_t visited_type;      // 0 - visited as ascending, 1 - visited as descending
  ADDRESS source_node;       // initiator of route request
  uint8_t route_id;          // route identifier at source

  uint8_t min_cost;          // used for tiebreaking of multiple paths for the same request
} PACKING_MACRO ROUTEBUFFER_ENTRY;

typedef struct {
  uint8_t failed;
  ADDRESS source_node, dest_node;

  uint8_t path_len;
  ADDRESS *path;

  uint8_t data_len;
  uint8_t *data;
} PACKING_MACRO PAYLOADBUFFER_ENTRY;

ROUTEBUFFER_ENTRY route_buffer[ROUTEBUFFER_SIZE];
uint8_t routebuffer_size;            // how many entries used on the route buffer
uint8_t route_seq;

PAYLOADBUFFER_ENTRY payload_buffer[PAYLOADBUFFER_SIZE];
uint8_t payloadbuffer_first, payloadbuffer_last, payloadbuffer_size;

void init_mroute();

int neighborshortcut_index(ATTRIBUTE id);

uint8_t init_route(uint8_t predicate_count, ROUTING_PREDICATE *predicates, uint8_t method,
  uint8_t data_len, uint8_t *data);
uint8_t init_repairroute(ADDRESS dest_node, ADDRESS failed_node, uint8_t path_len, ADDRESS *path,
  uint8_t data_len, uint8_t *data);
uint8_t init_baseflood(uint8_t predicate_count, ROUTING_PREDICATE *predicates, uint8_t data_len,
  uint8_t *data);

void handle_msghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);
void handlefail_msghi(ADDRESS source_node, ADDRESS dest_node, uint8_t path_nodecount,
  ADDRESS *path_nodes, uint8_t data_len, uint8_t *data);

void seq_route(ADDRESS source_id, ROUTE_MSG *msg);
void handle_seqroutemsg(ADDRESS source_id, uint8_t *p);
void handlesent_seqroutemsg(uint8_t msg_len, uint8_t *p, uint8_t send_success, uint8_t node_count,
  ADDRESS *nodes, uint8_t confirm_seq);

void flood_route(ADDRESS source_id, ROUTE_MSG *msg);
void handle_floodroutemsg(ADDRESS source_id, uint8_t *p);
uint8_t checkaccept_floodroutemsg(ADDRESS source_id, uint8_t *p);

void base_flood(ADDRESS source_id, BASEFLOOD_MSG *msg);
void handle_basefloodmsg(ADDRESS source_id, uint8_t *p);
uint8_t checkaccept_basefloodmsg(ADDRESS source_id, uint8_t *p);

void update_payloadbuffer();
void enqueue_payload(ADDRESS source_node, ADDRESS dest_node, uint8_t path_len, ADDRESS *path,
  uint8_t data_len, uint8_t *data, uint8_t failed);
